//TODO: Hitandmiss: test

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// HitMiss is an algorithm to 'hit and miss' a 
    /// binary image using a 3x3 kernel.
    /// </summary>
    public class HitAndMiss : BaseForeBackgroundColorFilter
    {
        protected int[] m_aKernel = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1 };

        /// <summary>
        /// Default no-args constructor.
        /// </summary>
        public HitAndMiss()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HitAndMiss"/> class.
        /// </summary>
        /// <param name="_aKernel">The _a kernel.</param>
        public HitAndMiss(int[] _aKernel)
        {
            this.m_aKernel = _aKernel;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HitAndMiss"/> class.
        /// </summary>
        /// <param name="_hitAndMiss">The _hit and miss.</param>
        internal HitAndMiss(HitAndMiss _hitAndMiss)
        {
            this.m_aKernel = _hitAndMiss.m_aKernel;
        }

        /// <summary>
        /// Gets or sets the kernel.
        /// </summary>
        /// <value>The kernel.</value>
        public int[] Kernel
        {
            get { return m_aKernel; }
            set { m_aKernel = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;
            this.HitmissImage(bitmap, m_aKernel);
            return _bitmap;
        }

        /// <summary>
        /// Returns true if the 8 neighbours of p match the kernel
        /// 0 is background
        /// 1 is foreground
        /// 2 is don't care.
        /// </summary>
        /// <param name="p">the point at the centre of the</param>
        /// <param name="pixels">9 pixel neighbourhood</param>
        /// <param name="w">the width of the image</param>
        /// <param name="h">the height of the image</param>
        /// <param name="_aKernel">The _a kernel.</param>
        /// <returns>True if the kernel and image match.</returns>
        private bool KernelMatch(Point p, UnsafeBitmap pixels,
                       int w, int h, int[] _aKernel)
        {
            int matched = 0;
            for (int j = -1; j < 2; ++j)
            {
                for (int i = -1; i < 2; ++i)
                {
                    if (_aKernel[((j + 1) * 3) + (i + 1)] == 2)
                    {
                        ++matched;
                    }
                    else if ((p.X + i >= 0) && (p.X + i < w) && (p.Y + j >= 0) && (p.Y + j < h) &&
                       (((pixels.GetPixel(p.X + i, p.Y + j) == m_ColorForeground) &&
                         (_aKernel[((j + 1) * 3) + (i + 1)] == 1)) ||
                        ((pixels.GetPixel(p.X + i, p.Y + j) == m_ColorBackground) &&
                         (_aKernel[((j + 1) * 3) + (i + 1)] == 0))))
                    {
                        ++matched;
                    }
                }
            }
            if (matched == 9)
            {
                return true;
            }
            else return false;
        }

        /// <summary>
        /// Applies the hitmiss operation to a set of pixels
        /// stored in a hash table.
        /// </summary>
        /// <param name="b">the BinaryFast input image</param>
        /// <param name="_hashSetInput">The _hash set input.</param>
        /// <param name="_aKernel">The _a kernel.</param>
        /// <returns>
        /// A hash table containing all the successful matches.
        /// </returns>
        public HashSet HitMissHashSet(UnsafeBitmap b,
                             HashSet _hashSetInput,
                             int[] _aKernel)
        {
            HashSet output = new HashSet();
            IEnumerator it = _hashSetInput.GetEnumerator();
            while (it.MoveNext())
            {
                Point p = (Point)it.Current;
                if (KernelMatch((Point)p, b, b.Width, b.Height, _aKernel))
                {
                    //System.out.println("Match "+p.x+" "+p.y);
                    output.Add(p);
                }
            }
            //System.out.println(output.size());
            return output;
        }

        /// <summary>
        /// Returns true if the 3x3 kernel consists of 9 1s.
        /// </summary>
        /// <param name="kernel">kernel the array storing the 9 values</param>
        /// <returns>True if all 1s (false otherwise)</returns>
        private bool KernelAll1s(int[] kernel)
        {
            for (int i = 0; i < 9; ++i)
            {
                if (kernel[i] == 0) return false;
            }
            return true;
        }

        /// <summary>
        /// Returns true if the 3x3 kernel consists of 9 0s.
        /// </summary>
        /// <param name="kernel">the array storing the 9 values</param>
        /// <returns>True if all 0s (false otherwise)</returns>
        private bool KernelAll0s(int[] kernel)
        {
            for (int i = 0; i < 9; ++i)
            {
                if (kernel[i] == 1) return false;
            }
            return true;
        }

        /// <summary>
        /// Returns true if the 3x3 kernel has no 0s.
        /// </summary>
        /// <param name="kernel">kernel the array storing the 9 values</param>
        /// <returns>True if no 0s (false otherwise)</returns>
        public bool KernelNo0s(int[] kernel)
        {
            for (int i = 0; i < 9; ++i)
            {
                if (kernel[i] == 0) return false;
            }
            return true;
        }

        /// <summary>
        /// Returns true if the 3x3 kernel has no 1s.
        /// </summary>
        /// <param name="kernel">the array storing the 9 values</param>
        /// <returns>True if no 1s (false otherwise)</returns>
        private bool KernelNo1s(int[] kernel)
        {
            for (int i = 0; i < 9; ++i)
            {
                if (kernel[i] == 1) return false;
            }
            return true;
        }

        /// <summary>
        /// Takes a BinaryFast image representation and a kernel and
        /// applies the hitmiss algorithm to the image.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="kernel">kernel the kernel in 1D array form</param>
        /// <returns>
        /// The new BinaryFast image after hitmissing
        /// </returns>
        private UnsafeBitmap HitmissImage(UnsafeBitmap _bitmap,
                           int[] kernel)
        {
            HashSet input = new HashSet();
            HashSet result = new HashSet();
            IEnumerator it = null;
            Point p = Point.Empty;

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            //if kernel is all1s then simply remove foreEdge from fore
            //if kernel is all0s then flip image
            //if kernel is all1sAndAnys [4] 1 then add all fore
            //if kernel is all0sAndAnys [4] 0 then make input all back pixels
            //if kernel is mixed 1s0s (and/or anys) and [4] is 1 then use fore edge
            //if kernel is mixed 1s0s and [4] is 0 then use back edge
            //if kernel [4] is any then use all pixels
            if (KernelNo1s(kernel) && KernelNo0s(kernel))
            {
                for (int j = 0; j < nHeight; ++j)
                {
                    for (int i = 0; i < nWidth; ++i)
                    {
                        _bitmap.SetPixel(i, j, m_ColorForeground);
                    }
                }
            }
            else
            {
                if (kernel[4] == 1)
                {
                    for (int j = 0; j < nHeight; ++j)
                    {
                        for (int i = 0; i < nWidth; ++i)
                        {
                            if (_bitmap.GetPixel(i, j) == m_ColorForeground)
                            {
                                p = new Point(i, j);
                                input.Add(p);
                            }
                        }
                    }
                }
                else if (kernel[4] == 0)
                {
                    for (int j = 0; j < nHeight; ++j)
                    {
                        for (int i = 0; i < nWidth; ++i)
                        {
                            if (_bitmap.GetPixel(i, j) == m_ColorBackground)
                            {
                                p = new Point(i, j);
                                input.Add(p);
                            }
                        }
                    }
                }
                else
                {
                    for (int j = 0; j < nHeight; ++j)
                    {
                        for (int i = 0; i < nWidth; ++i)
                        {
                            p = new Point(i, j);
                            input.Add(p);
                        }
                    }
                }
                result = new HashSet(HitMissHashSet(_bitmap, input, kernel));
                Debug.WriteLine(result.Count);
                _bitmap.GeneratePixels(result);
            }
            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HitAndMiss(this);
        }
    }
}